﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ProxyCrawler.Entity;
using Quartz;
using CommonUtil.Common;
using CommonUtil.Common.Helpers;
using CommonUtil.Common.Log;
using CommonUtil.Extensions;
using System.IO;
using Newtonsoft.Json;
using System.Text;

namespace ProxyCrawler.Job
{
    public class SyncProxyJob : BaseQuartzJob
    {
        private readonly IReadOnlyCollection<IProxyProvider> _proxyProviders;

        string strResultName = "Data\\data.json";

        public SyncProxyJob() : this(DependencyResolver.Current.ResolveServices<IProxyProvider>().ToArray())
        {
        }

        public SyncProxyJob(IReadOnlyCollection<IProxyProvider> proxyProviders) : base(LogHelper.GetLogHelper<SyncProxyJob>()) => _proxyProviders = proxyProviders;

        protected override async Task ExecuteAsync(IJobExecutionContext context)
        {
            List<ProxyEntity> ips = (await Task.WhenAll(_proxyProviders.Select(_ => _.SyncProxyIp()))).SelectMany(_ => _).ToList();

            ips.AddRange(ReadData());
            if (ips.Count > 0)
            {
                //验证代理可用性
                var result = SaveProxy(
                    await ValidateProxyAsync(ips.Distinct(new ProxyEntityEqualityComparer()).ToArray())
                    );
                if (result > 0)
                {
                    Logger.Info("代理同步成功");
                }
                else
                {
                    Logger.Warn("代理同步失败");
                }
            }
        }

        /// <summary>
        /// 保存代理
        /// </summary>
        /// <param name="proxyIpEntities"></param>
        /// <returns></returns>
        private int SaveProxy(ProxyEntity[] proxyIpEntities)
        {
            if (proxyIpEntities.Length == 0)
            {
                Logger.Info("没有可用的代理");
                return 0;
            }
            Logger.Info($"可用代理IP数量：{proxyIpEntities.Length}");
            SaveData(proxyIpEntities.ToList());
            return 1;
        }

        /// <summary>
        /// 检查代理是否有效，过滤掉无效的
        /// </summary>
        /// <param name="proxyList"></param>
        /// <returns></returns>
        private async Task<ProxyEntity[]> ValidateProxyAsync(IReadOnlyCollection<ProxyEntity> proxyList)
        {
#if DEBUG
            await Task.Run(() => proxyList.ForEach(p => p.IsValid = true));
#else

            await Task.WhenAll(proxyList.Select(ValidateProxyAsync));
#endif
            return proxyList.Where(_ => _.IsValid).ToArray();
        }

        /// <summary>
        /// 验证一个代理时候有效
        /// </summary>
        /// <param name="proxyEntity"></param>
        /// <returns></returns>
        private async Task ValidateProxyAsync(ProxyEntity proxyEntity)
        {
            var client = new HttpRequestClient("https://baidu.com");
            client.AddProxy(new WebProxy(proxyEntity.Ip, proxyEntity.Port));
            HttpWebResponse response = null;
            try
            {
                response = await RetryHelper.TryInvokeAsync(() => client.ExecuteForResponseAsync(), res => res.StatusCode == HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                Logger.Warn($"验证代理【{proxyEntity.Ip}:{proxyEntity.Port}】发生异常", ex);
            }
            if (response?.StatusCode != null)
            {
                proxyEntity.IsValid = true;
                Logger.Info($"验证代理【{proxyEntity.Ip}:{proxyEntity.Port}】,response StatusCode:{response.StatusCode}");
            }
        }

        void SaveData(List<ProxyEntity> proxyIps)
        {
            string strfp = AppDomain.CurrentDomain.BaseDirectory + strResultName;
            FileInfo fiData = new FileInfo(strfp);
            if(fiData.Exists)
            {
                try
                {
                    using (var writer = new StreamWriter(fiData.FullName, false, Encoding.UTF8))
                    {
                        writer.Write(JsonConvert.SerializeObject(proxyIps));
                    }
                }
                catch (Exception exp)
                {
                    Logger.Error(string.Format("写入配置文件<{0}>发生异常", strfp));
                }
            }
            else
            {
                FileStream fileStream = new FileStream(strfp, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            }
        }

        List<ProxyEntity> ReadData()
        {
            string strfp = AppDomain.CurrentDomain.BaseDirectory + strResultName;
            FileInfo fiData = new FileInfo(strfp);
            List<ProxyEntity> proxyIps = new List<ProxyEntity>();
            if (fiData.Exists)
            {
                try
                {
                    using (var reader =
                        new StreamReader(fiData.Open(FileMode.Open, FileAccess.Read, FileShare.Read),
                            Encoding.UTF8))
                    {
                        var configContent = reader.ReadToEnd();
                        proxyIps = JsonConvert.DeserializeObject<List<ProxyEntity>>(configContent);
                    }
                }
                catch (Exception exp)
                {
                    Logger.Error(string.Format("读取配置文件<{0}>发生异常", strfp));
                }
            }
            else
            {
                FileStream fileStream = new FileStream(strfp, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            }
            return proxyIps;
        }
    }
}
